namespace Angelus.Data.Linq.ProviderBase
{
    using System.Collections.Generic;
    using System.Data.DLinq.Provider;
    using System.Data.DLinq.ProviderBase;

    internal class OracleFlattener
    {
        // Methods
        private readonly Visitor _visitor;

        internal OracleFlattener()
        {
            this._visitor = new Visitor();
        }


        internal SqlNode Flatten(SqlNode node)
        {
            return this._visitor.Visit(node);
        }


        // Fields

        // Nested Types

        #region Nested type: Visitor

        private class Visitor : SqlVisitor
        {
            // Methods
            private bool isInput;
            private bool isUpdate;
            private SqlRow row;
            private bool topLevel;

            internal Visitor()
            {
                this.topLevel = true;
                this.isInput = false;
                this.isUpdate = false;
            }


            private SqlColumn FindColumn(IEnumerable<SqlColumn> columns, SqlColumn col)
            {
                foreach (SqlColumn column1 in columns)
                {
                    if (base.RefersToColumn(column1, col))
                    {
                        return column1;
                    }
                }
                return null;
            }

            private static SqlColumn FindColumnWithExpression(IEnumerable<SqlColumn> columns, SqlExpression expr)
            {
                SqlColumnRef ref2 = expr as SqlColumnRef;
                foreach (SqlColumn column1 in columns)
                {
                    SqlColumnRef ref3 = column1.Expression as SqlColumnRef;
                    if ((ref2 != null) && ((ref2.Column == column1) || ref2.Equals(ref3)))
                    {
                        return column1;
                    }
                    if (SqlComparer.AreEqual(column1.Expression, expr))
                    {
                        return column1;
                    }
                }
                return null;
            }

            private SqlExpression Flatten(SqlExpression sex, string name)
            {
                if (sex == null)
                {
                    return null;
                }
                SqlNodeType type1 = sex.NodeType;
                if (type1 <= SqlNodeType.Grouping)
                {
                    switch (type1)
                    {
                        case SqlNodeType.Link:
                            {
                                SqlLink link1 = (SqlLink) sex;
                                int num7 = 0;
                                int num8 = link1.Expressions.Count;
                                while (num7 < num8)
                                {
                                    link1.Expressions[num7] = this.Flatten(link1.Expressions[num7], null);
                                    num7++;
                                }
                                return link1;
                            }
                        case SqlNodeType.Grouping:
                            {
                                SqlGrouping grouping1 = (SqlGrouping) sex;
                                grouping1.Key = this.Flatten(grouping1.Key, "Key");
                                grouping1.Group = this.Flatten(grouping1.Group, "GroupSequence");
                                return grouping1;
                            }
                        case SqlNodeType.ClientArray:
                            {
                                SqlClientArray array1 = (SqlClientArray) sex;
                                int num5 = 0;
                                int num6 = array1.Expressions.Count;
                                while (num5 < num6)
                                {
                                    array1.Expressions[num5] = this.Flatten(array1.Expressions[num5], null);
                                    num5++;
                                }
                                return array1;
                            }
                        case SqlNodeType.ClientCase:
                            {
                                SqlClientCase case1 = (SqlClientCase) sex;
                                case1.Expression = this.Flatten(case1.Expression, name);
                                foreach (SqlClientWhen when1 in case1.Whens)
                                {
                                    when1.Value = this.Flatten(when1.Value, name);
                                }
                                return case1;
                            }
                        case SqlNodeType.ClientCollection:
                        case SqlNodeType.ClientElement:
                            {
                                SqlClientQuery query1 = (SqlClientQuery) sex;
                                query1.Count = this.Flatten(query1.Count, name);
                                return query1;
                            }
                        case SqlNodeType.ClientExpression:
                            if (!this.isInput)
                            {
                                return sex;
                            }
                            goto Label_0461;

                        case SqlNodeType.ClientParameter:
                        case SqlNodeType.Coalesce:
                            goto Label_0461;

                        case SqlNodeType.Column:
                            {
                                SqlColumn column1 = (SqlColumn) sex;
                                SqlColumn column2 = this.FindColumn(this.row.Columns, column1);
                                if (column2 == null)
                                {
                                    this.row.Columns.Add(column1);
                                    column2 = column1;
                                }
                                return new SqlColumnRef(column2);
                            }
                        case SqlNodeType.ColumnRef:
                            {
                                SqlColumnRef ref1 = (SqlColumnRef) sex;
                                SqlColumn column3 = this.FindColumn(this.row.Columns, ref1.Column);
                                if (column3 != null)
                                {
                                    return new SqlColumnRef(column3);
                                }
                                goto Label_0461;
                            }
                        case SqlNodeType.AliasRef:
                            return sex;
                    }
                }
                else if (type1 <= SqlNodeType.OptionalValue)
                {
                    switch (type1)
                    {
                        case SqlNodeType.New:
                            {
                                SqlNew new1 = (SqlNew) sex;
                                int num1 = 0;
                                int num2 = new1.Args.Count;
                                while (num1 < num2)
                                {
                                    new1.Args[num1] = this.Flatten(new1.Args[num1],
                                                                   new1.Constructor.GetParameters()[num1].Name);
                                    num1++;
                                }
                                foreach (SqlMemberAssign assign1 in new1.Members)
                                {
                                    string text1 = assign1.Member.Name;
                                    if (this.isInput || this.isUpdate)
                                    {
                                        text1 = new1.MetaType.GetDataMember(assign1.Member).MappedName;
                                    }
                                    assign1.Expression = this.Flatten(assign1.Expression, text1);
                                }
                                return new1;
                            }
                        case SqlNodeType.Not:
                        case SqlNodeType.Or:
                            goto Label_0461;

                        case SqlNodeType.Nop:
                            return sex;

                        case SqlNodeType.OptionalValue:
                            {
                                SqlOptionalValue value1 = (SqlOptionalValue) sex;
                                this.Flatten(value1.HasValue, "test");
                                this.Flatten(value1.Value, null);
                                return value1;
                            }
                        case SqlNodeType.MethodCall:
                            {
                                SqlMethodCall call1 = (SqlMethodCall) sex;
                                call1.Object = this.Flatten(call1.Object, null);
                                int num3 = 0;
                                int num4 = call1.Arguments.Count;
                                while (num3 < num4)
                                {
                                    call1.Arguments[num3] = this.Flatten(call1.Arguments[num3], null);
                                    num3++;
                                }
                                return call1;
                            }
                    }
                }
                else if ((type1 != SqlNodeType.SimpleCase) && (type1 == SqlNodeType.TypeCase))
                {
                    SqlTypeCase case2 = (SqlTypeCase) sex;
                    if (!this.isInput && !this.isUpdate)
                    {
                        case2.Discriminator = this.Flatten(case2.Discriminator, name);
                    }
                    foreach (SqlTypeCaseWhen when2 in case2.Whens)
                    {
                        when2.TypeBinding = this.Flatten(when2.TypeBinding, name);
                    }
                    return case2;
                }
                Label_0461:
                SqlColumn column4 = FindColumnWithExpression(this.row.Columns, sex);
                if (column4 == null)
                {
                    column4 = new SqlColumn(sex.ClrType, sex.SqlType, name, sex, sex.SourceExpression);
                    this.row.Columns.Add(column4);
                }
                return new SqlColumnRef(column4);
            }

            private void FlattenGroupBy(List<SqlExpression> exprs)
            {
                List<SqlExpression> list2 = new List<SqlExpression>(exprs.Count);
                foreach (SqlExpression expression1 in exprs)
                {
                    this.FlattenGroupByExpression(list2, expression1);
                }
                exprs.Clear();
                exprs.AddRange(list2);
            }

            private void FlattenGroupByExpression(ICollection<SqlExpression> exprs, SqlExpression expr)
            {
                SqlNew new2 = expr as SqlNew;
                if (new2 != null)
                {
                    MetaType type1 = new2.MetaType;
                    if (type1.IsEntity)
                    {
                        foreach (SqlMemberAssign assign1 in new2.Members)
                        {
                            MetaDataMember member1 = type1.GetDataMember(assign1.Member);
                            if (member1.IsIdentity)
                            {
                                this.FlattenGroupByExpression(exprs, assign1.Expression);
                            }
                        }
                    }
                    else
                    {
                        foreach (SqlMemberAssign assign2 in new2.Members)
                        {
                            this.FlattenGroupByExpression(exprs, assign2.Expression);
                        }
                    }
                }
                else if (expr.NodeType == SqlNodeType.TypeCase)
                {
                    SqlTypeCase case1 = (SqlTypeCase) expr;
                    this.FlattenGroupByExpression(exprs, case1.Discriminator);
                    foreach (SqlTypeCaseWhen when1 in case1.Whens)
                    {
                        this.FlattenGroupByExpression(exprs, when1.TypeBinding);
                    }
                }
                else if (expr.NodeType != SqlNodeType.Literal)
                {
                    exprs.Add(expr);
                }
            }

            private void FlattenOrderBy(List<SqlOrderExpression> exprs)
            {
                List<SqlOrderExpression> list2 = new List<SqlOrderExpression>(exprs.Count);
                foreach (SqlOrderExpression expression1 in exprs)
                {
                    this.FlattenOrderByExpression(list2, expression1.Expression, expression1.OrderType);
                }
                exprs.Clear();
                exprs.AddRange(list2);
            }

            private void FlattenOrderByExpression(ICollection<SqlOrderExpression> exprs, SqlExpression expr,
                                                  SqlOrderType otype)
            {
                SqlNew new2 = expr as SqlNew;
                if (new2 != null)
                {
                    MetaType type1 = new2.MetaType;
                    if (type1.IsEntity)
                    {
                        foreach (SqlMemberAssign assign1 in new2.Members)
                        {
                            MetaDataMember member1 = type1.GetDataMember(assign1.Member);
                            if (member1.IsIdentity)
                            {
                                this.FlattenOrderByExpression(exprs, assign1.Expression, otype);
                            }
                        }
                    }
                    else
                    {
                        foreach (SqlMemberAssign assign2 in new2.Members)
                        {
                            this.FlattenOrderByExpression(exprs, assign2.Expression, otype);
                        }
                    }
                }
                else if (expr.NodeType == SqlNodeType.TypeCase)
                {
                    SqlTypeCase case1 = (SqlTypeCase) expr;
                    this.FlattenOrderByExpression(exprs, case1.Discriminator, otype);
                    foreach (SqlTypeCaseWhen when1 in case1.Whens)
                    {
                        this.FlattenOrderByExpression(exprs, when1.TypeBinding, otype);
                    }
                }
                else if (expr.NodeType != SqlNodeType.Literal)
                {
                    exprs.Add(new SqlOrderExpression(otype, expr));
                }
            }

            internal override SqlExpression VisitColumn(SqlColumn col)
            {
                this.row.Columns.Add(col);
                return new SqlColumnRef(col);
            }

            internal override SqlStatement VisitInsert(SqlInsert sin)
            {
                SqlRow row2 = this.row;
                bool flag2 = this.isInput;
                this.row = sin.Row;
                this.isInput = true;
                base.VisitInsert(sin);
                sin.Expression = this.Flatten(sin.Expression, null);
                this.isInput = flag2;
                this.row = row2;
                return sin;
            }

            internal override SqlSelect VisitSelect(SqlSelect select)
            {
                SqlRow row2 = this.row;
                bool flag3 = this.topLevel;
                bool flag4 = this.isInput;
                this.row = select.Row;
                this.topLevel = false;
                this.isInput = false;
                select = base.VisitSelect(select);
                this.topLevel = flag3;
                select.Selection = this.Flatten(select.Selection, null);
                this.isInput = flag4;
                this.row = row2;
                if (select.GroupBy.Count > 0)
                {
                    this.FlattenGroupBy(select.GroupBy);
                }
                if (select.OrderBy.Count > 0)
                {
                    this.FlattenOrderBy(select.OrderBy);
                }
                if (!flag3)
                {
                    select.Selection = new SqlNop(select.Selection.ClrType, select.Selection.SqlType,
                                                  select.SourceExpression);
                }
                return select;
            }

            internal override SqlStatement VisitUpdate(SqlUpdate sud)
            {
                bool flag2 = this.isUpdate;
                this.isUpdate = true;
                base.VisitUpdate(sud);
                this.isUpdate = flag2;
                return sud;
            }
        }

        #endregion
    }
}